﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using centrafuse.Plugins;
using System.Windows.Forms;
using iTunesLib;
using System.Drawing;
using System.IO;

namespace CFiTunes
{
    public class CFiTunes : CFPlugin
    {
        //System path variables
        private const string PluginName = "CFiTunes";
        private const string PluginPath = @"plugins\" + PluginName + @"\";
        private const string PluginPathSkins = PluginPath + @"Skins\";
        private const string PluginPathLanguages = PluginPath + @"Languages\";
        private const string PluginPathIcons = PluginPath + @"Icons\";
        private const string ConfigurationFile = "config.xml";
        private const string LogFile = "cf_itunes.log";
        string art_cache_path = CFTools.AppDataPath + "\\Plugins\\" + PluginName + "\\" + "cache" + "\\";

        // we write the log to the appropriate users local appdata directory in the Plugins subfolder...
        public static string LogFilePath = CFTools.AppDataPath + "\\Plugins\\" + PluginName + "\\" + LogFile;
        public static string settingsPath = CFTools.AppDataPath + "\\system\\settings.xml";
        public static Boolean _loggingOn = true;
        public static Boolean _onStart = true;

        private iTunesApp _itunes = null;
        private IITTrack _currentTrack = null;
        private IITArtwork _artwork = null;

        private bool _hasControl = false;
        
        private delegate void voidDelegate();
        private delegate void intDelegate(int n);
        //private delegate void stringDelegate(string s);
        //private delegate void exceptionDelegate(Exception ex);


        public CFiTunes()
        {
            // nothing special to do here
            // in a more advanced plugin, flags and such can be done here
            // Usually it is safe to just use the CF_initPlugin() override to do initialization
        }

        public override void CF_pluginInit()
        {
            CFTools.writeModuleLog("CFiTunes: CF_pluginInit", LogFilePath);
            WriteLog("starting");
            try
            {
                WriteLog("CF_pluginInit");
                // 
                this.CF3_initPlugin("CFiTunes", true);
                this.CF_params.Media.isAudioPlugin = true;
                this.CF_params.hasBasicSettings = true;
                //this.CF_params.pauseAudioVisible = true;

                this.CF_params.isGUI = true;

                this.CF_params.Media.useCorePlaybackControl = false;
                //The useCorePlaybackControl (defaults to true) is used to tell Centrafuse whether or not the plugin utilizes
                //    the internal buffer/non-buffer system (i.e. SatRadio).
                //    Plugins which utilize their own audio api’s (i.e. Pandora) need to set this flag to false.
                //    This flag needs to be set in the constructor or CF_pluginInit(), it only needs to be set once.

                this.CF_params.Media.visibleOnly = false;       // Audio can continue to play, even if plugin is not visible

                // All controls should be created or Setup in CF_localskinsetup.
                // This method is also called when the resolution or skin has changed.
                this.CF_localskinsetup();

                WriteLog("Wiring Up Plugin Events");
                this.CF_events.trackPositionChanged += new CFTrackPositionChangedEventHandler(CF_events_trackPositionChanged);

                // loads Settings
                LoadSettings();
                loadiTunes();

                // add event handlers for keyboard and power mode change
                //this.CF_Event_powerModeChanged += new Microsoft.Win32.PowerModeChangedEventHandler(HelloWorld_CF_Event_powerModeChanged);
                //this.KeyDown += new KeyEventHandler(HelloWorld_KeyDown);
            }
            catch (Exception errmsg)
            {
                CF_displayMessage("Error initializing CFiTunes plugin: " + errmsg);
                CFTools.writeError(errmsg.ToString());
            }
            WriteLog("done CF_pluginInit");
        }

        private void CF_events_trackPositionChanged(object sender, TrackPositionChangedArgs e)
        {
            WriteLog("CF_events_trackPositionChanged fired!");
            BeginInvoke(new intDelegate(setPosition), new object[] { e.Percentage() });
        }

        public void setPosition(int positionPercentage)
        {
            WriteLog("setPosition to: " + positionPercentage.ToString());
            var totalLength = _currentTrack.Duration;
            var position = Math.Floor((double)positionPercentage * (double)totalLength / 100.0);

            if (position > totalLength)
                position = totalLength;

            _itunes.PlayerPosition = (int)position;
        }

        private void loadiTunes()
        {
            WriteLog("iTunes finding or starting...");
            try
            {
                _itunes = (iTunesApp)System.Runtime.InteropServices.Marshal.GetActiveObject("iTunesApp");
            }
            catch (System.Runtime.InteropServices.COMException ex)
            {
                WriteLog(string.Format("Running iTunes not found. Error: {0}", ex.Message));
                WriteLog("iTunes starting...");
                _itunes = new iTunesApp();
            }
            if (_itunes == null)
            {
                throw new Exception("iTunes ERROR");
            }
            else
            {
                WriteLog("iTunes started");
            }

            // Add Event Handlers
            //onDatabaseChangedEvent
            _itunes.OnPlayerPlayEvent += new _IiTunesEvents_OnPlayerPlayEventEventHandler(myiTunes_OnPlayerPlayEvent);
            _itunes.OnPlayerStopEvent += new _IiTunesEvents_OnPlayerStopEventEventHandler(myiTunes_OnPlayerStopEvent);
            _itunes.OnPlayerPlayingTrackChangedEvent += new _IiTunesEvents_OnPlayerPlayingTrackChangedEventEventHandler(myiTunes_OnPlayerPlayingTrackChangedEvent);
            // _itunes.OnAboutToPromptUserToQuitEvent += new _IiTunesEvents_OnAboutToPromptUserToQuitEventEventHandler(myiTunes_OnAboutToPromptUserToQuitEvent);
            //onCOMCallsDisabledEvent
            //onCOMCallsEnabledEvent
            //onQuittingEvent
            // _itunes.OnSoundVolumeChangedEvent += new _IiTunesEvents_OnSoundVolumeChangedEventEventHandler(myiTunes_OnSoundVolumeChangedEvent);

            setTrackInfo();
        }

        private void myiTunes_OnPlayerPlayEvent(object iTrack)
        {
            WriteLog("itunes track play event!");
            _currentTrack = (IITTrack)iTrack;
            setTrackInfo();
        }

        private void myiTunes_OnPlayerStopEvent(object iTrack)
        {
            WriteLog("itunes track stop event!");
            _currentTrack = (IITTrack)iTrack;
            setTrackInfo();
        }

        private void myiTunes_OnPlayerPlayingTrackChangedEvent(object iTrack)
        {
            WriteLog("itunes track changed event!");
            _currentTrack = (IITTrack)iTrack;
            setTrackInfo();
        }

        public override void CF_pluginShow()
        {
            try
            {
                //this.CF_params.Media.mediaPlaying = false;
                // You must call the base implementation below
                base.CF_pluginShow(); // Sets form Visible property to true
                _hasControl = true;
            }
            catch (Exception ex)
            {
                _hasControl = false;
                CFTools.writeError(ex.ToString());
            }
        }

        public override void CF_pluginClose()
        {
            base.CF_pluginClose(); // calls form Dispose() method

            if (_itunes != null)
            {
                // Remove any handlers from the iTunes COM object.
                //onDatabaseChangedEvent
                _itunes.OnPlayerPlayEvent -= myiTunes_OnPlayerPlayEvent;
                _itunes.OnPlayerStopEvent -= myiTunes_OnPlayerStopEvent;
                _itunes.OnPlayerPlayingTrackChangedEvent -= myiTunes_OnPlayerPlayingTrackChangedEvent;
                // _itunes.OnAboutToPromptUserToQuitEvent -= myiTunes_OnAboutToPromptUserToQuitEvent;
                //onCOMCallsDisabledEvent
                //onCOMCallsEnabledEvent
                //onQuittingEvent
                // _itunes.OnSoundVolumeChangedEvent -= myiTunes_OnSoundVolumeChangedEvent;

                _itunes.Quit();
                _itunes = null;
            }
        }

        public override void CF_pluginPause()
        {
            base.CF_pluginPause();
            Pause();
            this.CF_params.Media.mediaPlaying = false;
            _hasControl = false;
        }

        public override void CF_pluginResume()
        {
            base.CF_pluginResume();
            Play();
            this.CF_params.Media.mediaPlaying = true;
            _hasControl = true;
        }

        private void LoadSettings()
        {
            // The display name is shown in the application to represent
            // the plugin.  This sets the display name from the configuration file.
            this.CF_params.displayName = this.pluginLang.ReadField("/APPLANG/CFITUNES/DISPLAYNAME");
            CFTools.writeLog("CFiTunes", "New display name = " + this.CF_params.displayName);
            Boolean.TryParse(this.pluginConfig.ReadField("/APPCONFIG/LOGEVENTS"), out _loggingOn);
            Boolean.TryParse(this.pluginConfig.ReadField("/APPCONFIG/STARTUPMUSIC"), out _onStart);
        }

        private void WriteLog(string msg)
        {
            //try
            //{
                //if (Boolean.Parse(this.pluginConfig.ReadField("/APPCONFIG/LOGEVENTS")))
                    CFTools.writeModuleLog(msg, LogFilePath);
            //}
            //catch { }
        }

        public override void CF_localskinsetup()
        { 
            // Read the skin file, controls will be automatically created
            this.CF3_initSection("CFiTunes");

            this.CF_createButtonClick("Eject", new MouseEventHandler(Import_Click));
        }

        public override DialogResult CF_pluginShowSetup()
        {
            CFTools.writeModuleLog("CFiTunes: CF_pluginShowSetup", LogFilePath);

            // Return DialogResult.OK for the main application to update from plugin changes.
            DialogResult returnvalue = DialogResult.Cancel;

            try
            {
                // Creates a new plugin setup instance. If you create a CFDialog or CFSetup you must
                // set its MainForm property to the main plugins MainForm property.
                Setup setup = new Setup(this.MainForm, pluginConfig, pluginLang);
                returnvalue = setup.ShowDialog();
                if (returnvalue == DialogResult.OK)
                {
                    // Reloads plugin configuration.
                    LoadSettings();
                }
                setup.Close();
                setup = null;
            }
            catch (Exception errmsg) { CFTools.writeError(errmsg.ToString()); }

            return returnvalue;
        }

        public override string CF_pluginCMLData(CF_CMLTextItems textitem)
        {
            // WriteLog("CF_pluginCMLData: " + textitem);
            switch (textitem)
            {
                case CF_CMLTextItems.MainTitle:
                    if (_currentTrack != null) { return _currentTrack.Name; } else { return "name"; }
                case CF_CMLTextItems.MediaTitle:
                    if (_currentTrack != null) { return _currentTrack.Name; } else { return "album"; }
                case CF_CMLTextItems.MediaArtist:
                    if (_currentTrack != null) { return _currentTrack.Artist; } else { return "artist"; }
                case CF_CMLTextItems.MediaAlbum:
                    if (_currentTrack != null) { return _currentTrack.Album; } else { return "artist"; }
                case CF_CMLTextItems.MediaDuration:
                    if (_currentTrack != null) { return formattedTime(_currentTrack.Duration); } else { return "duration"; }
                case CF_CMLTextItems.MediaPosition:
                    if (_currentTrack != null) { return formattedTime(_itunes.PlayerPosition); } else { return "position"; }
                case CF_CMLTextItems.MediaSliderPosition:
                    return GetCurrentTrackScrubberPosition();
                case CF_CMLTextItems.MediaStation:
                    return "MediaStation";
                case CF_CMLTextItems.MediaSource:
                    return "CFiTunes";
                //TrackDetails
                default:
                    WriteLog("unknown CF_pluginCMLData: " + textitem);
                    return base.CF_pluginCMLData(textitem);
            }
        }

        public override bool CF_pluginCMLCommand(string command, string[] strparams, CF_ButtonState state, int zone)
        {
            if (!_hasControl)
                return false;

            // By checking the state here, we only respond to button click events. Change as needed
            if (state != CF_ButtonState.Click)
                return false;

            WriteLog("CF_pluginCMLCommand: " + command);

            // Select something to do based on the command
            switch (command.ToUpper())
            {
                case "CENTRAFUSE.MAIN.PLAYPAUSE":
                    PlayPause();
                    return true;
                case "CENTRAFUSE.MAIN.REWIND":
                    PreviousTrack();
                    return true;
                case "CENTRAFUSE.MAIN.FASTFORWARD":
                    NextTrack();
                    return true;
                case "CENTRAFUSE.MEDIAPLAYER.TOGGLERANDOM":
                    ToggleRandom();
                    return true;
                case "CENTRAFUSE.MEDIAPLAYER.OPENMEDIAMANAGER":
                    WriteLog("OKOKOKOKOKOKOKOKOK");
                    return true;
                case "CENTRAFUSE.MEDIAPLAYER.ADDFAVORITE":
                    ToggleFavorite();
                    return true;
                case "CENTRAFUSE.MEDIAPLAYER.PAGEUP":
                    PreviousTrack();
                    return true;
                case "CENTRAFUSE.MEDIAPLAYER.PAGEDOWN":
                    NextTrack();
                    return true;
            }
            return false;
        }

        private string GetCurrentTrackScrubberPosition()
        {
            if (_currentTrack == null)
                return 0.ToString();

            var currentPosition = _itunes.PlayerPosition;
            var totalLength = _currentTrack.Duration;
            var positionPercentage = Math.Floor((double)currentPosition / (double)totalLength * 100.0);
            if (positionPercentage > 100)
                positionPercentage = 100;

            return ((int)positionPercentage).ToString();
        }

        private void PlayPause()
        {
            if (_itunes.PlayerState == ITPlayerState.ITPlayerStatePlaying)
            {
                Pause();
            }
            else
            {
                Play();
            }
        }

        public void Pause()
        {
            WriteLog("pausing");
            _itunes.Pause();
            CF_setPlayPauseButton(true, 0);
        }

        public void Play()
        {
            WriteLog("playing");
            _itunes.Play();
            CF_setPlayPauseButton(false, 0);
        }

        private void PreviousTrack()
        {
            _itunes.PreviousTrack();
        }

        private void NextTrack()
        {
            _itunes.NextTrack();
        }

        private void ToggleFavorite()
        {
            WriteLog("Toggleing favorite");
            if (_currentTrack == null)
                return;

            if (_currentTrack.Rating == 0)
            {
                _currentTrack.Rating = 100;
                base.CF_setButtonOn("AddFavorite");
            }
            else
            {
                _currentTrack.Rating = 0;
                base.CF_setButtonOff("AddFavorite");
            }
        }

        private void ToggleRandom()
        {
            WriteLog("Toggleing shuffle");
            if (_itunes.CurrentPlaylist == null)
                return;

            string random_path = CF_params.pluginSkinPath + "\\btn_shuffle.png";
            if (_itunes.CurrentPlaylist.Shuffle)
            {
                random_path = CF_params.pluginSkinPath + "\\btn_shuffle_off.png";
                _itunes.CurrentPlaylist.Shuffle = false;
            }
            else
            {
                _itunes.CurrentPlaylist.Shuffle = true;
            }
            base.CF_setPictureImage("Random", Image.FromFile(random_path));
        }

        private void setTrackInfo()
        {
            WriteLog("setTrackInfo");

            if (_currentTrack == null)
            {
                WriteLog("no current track for info");
                return;
            }

            WriteLog(string.Format("current_track = {0} ({1})", _currentTrack.Name, _currentTrack.trackID.ToString()));

            if (_itunes.CurrentPlaylist == null)
                return;
            
            // set rating
            if (_currentTrack.Rating == 100)
            {
                base.CF_setButtonOn("AddFavorite");
            }
            else
            {
                base.CF_setButtonOff("AddFavorite");
            }

            // set shuffle button
            string random_path = CF_params.pluginSkinPath + "\\btn_shuffle.png";
            if (_itunes.CurrentPlaylist.Shuffle)
            {
                random_path = CF_params.pluginSkinPath + "\\btn_shuffle_off.png";
            }
            base.CF_setPictureImage("Random", Image.FromFile(random_path));

            // set artwork
            Directory.CreateDirectory(art_cache_path);
            Image art_file = null;
            string art_path = art_cache_path + "artwork" + _currentTrack.trackID.ToString() + ".jpg";
            try
            {
                art_file = Image.FromFile(art_path);
            }
            catch (FileNotFoundException ex)
            {
                WriteLog(String.Format("artwork error: {0}", ex.Message));
            }
            if (art_file == null)
            {
                if (_currentTrack.Artwork != null && _currentTrack.Artwork.Count > 0)
                {
                    _artwork = _currentTrack.Artwork[1];
                    _artwork.SaveArtworkToFile(art_path);
                }
            }
            try
            {
                art_file = Image.FromFile(art_path);
            }
            catch (FileNotFoundException ex)
            {
                WriteLog(String.Format("artwork file error: {0}", ex.Message));
            }
            if (art_file != null)
            {
                base.CF_setPictureImage("CoverArt", art_file);
            }
            else
            {
                base.CF_setPictureImage("CoverArt", Image.FromFile(CF_params.pluginSkinPath + "\\audioart.png"));
            }
            //base.Invalidate();
        }

        private void Import_Click(object sender, MouseEventArgs e)
        {
            IITPlaylistCollection playlists = _itunes.LibrarySource.Playlists;

            WriteLog("playlists: " + playlists.Count.ToString());
            //CFDialogParams dialogParams = new CFDialogParams();
            //dialogParams.displaytext = String.Format("Are you sure you want to logoff of the guest account?");
            //DialogResult dialogResult = CF_displayDialog(CF_Dialogs.YesNo, dialogParams);

            //if (dialogResult == DialogResult.OK)
            //{

            //}

        }



        #region helpers

        private string formattedTime(int seconds)
        {
            int myMinutes = (seconds / 60);
            double mySeconds = (seconds % 60.0);
            return String.Format("{0}:{1:00}", myMinutes, mySeconds);
        }

        #endregion


    } // end of class
} // end of namespace
